#include "XiLexer.hpp"

#pragma warning(push)
#pragma warning(disable: 4390) // cause by "if (false) ;" in macro OP_BEGIN and SUBOP_BEGIN

#define APPEND()	token.m_text += m_char;
#define NEXT()		m_char = m_input.NextChar(&m_location)
#define SETKIND(tk)	token.m_kind = tk

#define SYMBOL_BEGIN(ch, tk) \
	case L##ch: \
		APPEND(); \
		NEXT(); \
		SETKIND(tk); \
		if (false) ;

#define SYMBOL_END() \
	break;

#define SYMBOL(ch, tk) \
	SYMBOL_BEGIN(ch, tk) \
	SYMBOL_END()

#define SUBOP_BEGIN(ch, tk) \
		else if (m_char == L##ch) \
		{ \
			APPEND(); \
			NEXT(); \
			SETKIND(tk); \
			if (false) ;

#define SUBOP_END() \
		}

#define SUBOP(ch, tk) \
	SUBOP_BEGIN(ch, tk) \
	SUBOP_END()

XI_NAMESPACE_BEGIN

XiLexer::XiLexer( XiCharStream &input )
: m_input(input)
, m_unicodeTable(XiUnicodeTable::Instance())
, m_keywordTable(XiKeywordTable::Instance())
{
	NEXT();	
}

void XiLexer::NextToken( XiToken &token )
{	
	token.m_kind = TK_UNINITIALIZED;
	token.m_text.clear();
	
	while (token.m_kind == TK_UNINITIALIZED)
	{ 
		token.m_location = m_location;

		switch (m_char)
		{
		case 0:
			SETKIND(TK_EOF);
			break;
		case L'\'': // 39
			NEXT();
			while (true)
			{
				if (m_char == 0)
				{
					// TODO: generate an error "end-of-file in string literal"
					break;
				}
				else if (m_char == L'\n') // 10
				{
					// TODO: generate an error "newline in string literal"
					break;
				}
				else if (m_char == L'\'') // 39
				{
					NEXT();
					break;
				}
				else if (m_char == L'\\') // 92
				{
					NEXT();
					ParseEscape(token);
				}
				else
				{
					APPEND();
					NEXT();
				}
			}
			SETKIND(TK_STRING_LITERAL);
			break;

		SYMBOL('%', TK_PERCENT) // 37
		SYMBOL('(', TK_LPAREN) // 40
		SYMBOL(')', TK_RPAREN) // 41
		SYMBOL(':', TK_COLON) // 58
		SYMBOL(';', TK_SEMICOLON) // 59
		SYMBOL('{', TK_LCURLY) // 123
		SYMBOL('}', TK_RCURLY) // 125

		SYMBOL_BEGIN('!', TK_EXCLAM) // 33
			SUBOP('=', TK_OP_NE)
		SYMBOL_END()
		SYMBOL_BEGIN('&', TK_AMPER) // 38
			SUBOP('&', TK_OP_LAND)
		SYMBOL_END()	
		SYMBOL_BEGIN('+', TK_PLUS) // 43
			SUBOP('+', TK_OP_INC)
		SYMBOL_END()
		SYMBOL_BEGIN('-', TK_MINUS) // 45
			SUBOP('-', TK_OP_DEC)
		SYMBOL_END()
		SYMBOL_BEGIN('<', TK_LT) // 60
			SUBOP('<', TK_OP_SHL)
		SYMBOL_END()
		SYMBOL_BEGIN('=', TK_EQUAL) // 61
			SUBOP('=', TK_OP_EQ)
		SYMBOL_END()
		SYMBOL_BEGIN('>', TK_GT) // 62
			SUBOP('>', TK_OP_SHR)
		SYMBOL_END()
		SYMBOL_BEGIN('|', TK_VERTBAR) // 124
			SUBOP('|', TK_OP_LOR)
		SYMBOL_END()

		default:
			if (m_char >= L'0' && m_char <= L'9')
			{
				do 
				{
					APPEND();
					NEXT();
				} while (m_char >= L'0' && m_char <= L'9');	
				SETKIND(TK_INTEGER_LITERAL);
			}
			else if (m_unicodeTable[m_char] & XI_UCP_XID_START)
			{
				do 
				{
					APPEND();
					NEXT();
				} while (m_unicodeTable[m_char] & XI_UCP_XID_CONTINUE);	
				SETKIND(TK_IDENTIFIER);
			}
			else if (m_unicodeTable[m_char] & XI_UCP_WHITE_SPACE)
			{
				do 
				{
					NEXT();
				} while (m_unicodeTable[m_char] & XI_UCP_WHITE_SPACE);	
			}
			else
				assert(false);
		}
	}
}

void XiLexer::ParseEscape( XiToken &token )
{
	switch (m_char)
	{
	case L'\'': // 39
		token.m_text += L'\'';
		break;
	case L'\\': // 92
		token.m_text += L'\\';
		break;
	case L'n': // 100
		token.m_text += L'\n';
		break;
	case L'r': // 114
		token.m_text += L'\r';
		break;
	default:
		assert(false);
	}
}

XI_NAMESPACE_END

#pragma warning(pop)